package game.square.temperature;
import java.math.*;

import be.kuleuven.cs.som.annotate.Value;

/**
 * An enumeration of temperaturescales.
 * At the moment it defines three scales: Celcius, kelvin 
 * and fahrenheit.
 * 
 * @version 1.0
 * @author Chris Desomer, Geert Heyman
 */
@Value
public enum TemperatureScale {

	CELCIUS, KELVIN, FAHRENHEIT ;
	
	/**
	 * Return the factor needed to convert the temperature from this temperature
	 * scale to the given target scale.
	 * 
	 * @param 	targetScale
	 * 		  	The temperature scale to convert to.
	 * @return	The method returns the factor to convert this
	 *  		temperature scale to the given temperature scale.
	 * @result  The resulting convert factor is effective.
	 * 		  | result != null
	 * @result 	The resulting convert factor is positive.
	 * 		  | result.signum() == 1
	 * @result 	If this temperature scale is the same as the other
	 * 		 	temperature scale, BigDecimal.ONE is returned.
	 * 		  | if (this == targetScale)
	 * 		  | 	then (result == BigDecimal.ONE)
	 * @result 	If this temperature scale is not the same as the other
	 * 			temperature scale, the resulting convert factor has the
	 * 			precision as established by the temperature scale context.
	 * 		  | if (this != other)
	 * 		  |   	then (result.precision() ==
	 * 		  | 	  temperatureScaleContext.getPrecision())
	 * @result 	The resulting convert factor is the inverse of
	 * 			the convert factor from the target temperature scale to this
	 * 			temperature scale.
	 * 		  | result.equals(BigDecimal.ONE.divide(temperatureScale.getConvertFactor(this),temperatureScaleContext))
	 * @throws 	IllegalArgumentException
	 * 			The given temperature scale is not effective.
	 * 		  |(targetScale == null)
	 */
	public BigDecimal getConvertFactor(TemperatureScale targetScale) throws IllegalArgumentException{
		if (targetScale == null)
			throw new IllegalArgumentException("The given targetScale is not effective.");
		if (convertFactors[this.ordinal()][targetScale.ordinal()]== null)
			convertFactors[this.ordinal()][targetScale.ordinal()] = BigDecimal.ONE
					.divide(convertFactors[targetScale.ordinal()][this.ordinal()],
							temperatureScaleContext);
		return convertFactors[this.ordinal()][targetScale.ordinal()];
	}
	
	/**
	 * Public class variable referencing the math context of temperature scale
	 * calculations.
	 */
	public static final MathContext temperatureScaleContext = 
		new MathContext(5,RoundingMode.HALF_DOWN);
	
	/**
	 * Return the factor needed to convert the temperature from this temperature
	 * scale to the given target scale.
	 * 
	 * @param 	targetScale
	 * 		  	The temperature scale to convert to.
	 * @return	The method returns the term to convert this temperature 
	 * 			scale to the given temperature scale, when using this formular:
	 * 			targetScaleValue = (currentScaleValue+(convert term))*(convert factor) 
	 * @result  The resulting convert term is effective.
	 * 		  | result != null
	 * @result 	The resulting convert term is positive.
	 * 		  | result.signum() == 1
	 * @result 	If this temperature scale is the same as the other
	 * 		 	temperature scale, BigDecimal.ZERO is returned.
	 * 		  | if (this == targetScale)
	 * 		  | 	then (result == BigDecimal.ZERO)
	 * @result 	If this temperature scale is not the same as the other
	 * 			temperature scale, the resulting convert term has the
	 * 			precision as established by the temperature scale context.
	 * 		  | if (this != other)
	 * 		  |   	then (result.precision() ==
	 * 		  | 	  temperatureScaleContext.getPrecision())
	 * @result 	The resulting convert term is the opposite of
	 * 			the term factor from the target temperature scale to this
	 * 			temperature scale.
	 * 		  | result.equals( temperatureScale.getConvertTerm(this).negate(temperatureScaleContext))
	 * @throws 	IllegalArgumentException
	 * 			The given temperature scale is not effective.
	 * 		  |(targetScale == null)
	 */
	public BigDecimal getConvertTerm(TemperatureScale targetScale) throws IllegalArgumentException {
		if (targetScale == null)
			throw new IllegalArgumentException("The given targetScale is not effective.");
		return convertTerms[this.ordinal()][targetScale.ordinal()];
	}
	
	/**
	* Variable referencing a two-dimensional array registering
	* the convert factors between scales. The first level is
	* indexed by the ordinal number of the temperature scale to 
	* convert from; the ordinal number to convert to is used to
	* index the second level.
	*/
	private static BigDecimal[][] convertFactors =
	new BigDecimal[3][3];
	static {
	// Initialization of the upper part of the exchange table.
	// Other rates are computed and registered the first time
	// they are queried.

	convertFactors[CELCIUS.ordinal()][CELCIUS.ordinal()] =
		BigDecimal.ONE;
	convertFactors[CELCIUS.ordinal()][KELVIN.ordinal()] =
		BigDecimal.ONE;
	convertFactors[CELCIUS.ordinal()][FAHRENHEIT.ordinal()] =
		new BigDecimal(BigInteger.valueOf(18),1);
	convertFactors[KELVIN.ordinal()][KELVIN.ordinal()] =
		BigDecimal.ONE;
	convertFactors[KELVIN.ordinal()][FAHRENHEIT.ordinal()] =
		new BigDecimal(BigInteger.valueOf(18),1);
	convertFactors[FAHRENHEIT.ordinal()][FAHRENHEIT.ordinal()] =
		BigDecimal.ONE;
	}

	
	private static BigDecimal[][] convertTerms = new BigDecimal[3][3];
	static {
	// Initialization of the upper part of the exchange table.
	// Other rates are computed and registered the first time
	// they are queried.

	convertTerms[CELCIUS.ordinal()][CELCIUS.ordinal()] =
		BigDecimal.ZERO;
	convertTerms[CELCIUS.ordinal()][KELVIN.ordinal()] =
		new BigDecimal(BigInteger.valueOf(27315),2);
	convertTerms[CELCIUS.ordinal()][FAHRENHEIT.ordinal()] =
		new BigDecimal(BigInteger.valueOf(17777),3);
	convertTerms[KELVIN.ordinal()][CELCIUS.ordinal()] =
		new BigDecimal(BigInteger.valueOf(-27315),2);
	convertTerms[KELVIN.ordinal()][KELVIN.ordinal()] =
		BigDecimal.ZERO;
	convertTerms[KELVIN.ordinal()][FAHRENHEIT.ordinal()] =
		new BigDecimal(BigInteger.valueOf(-25537),2);
	convertTerms[FAHRENHEIT.ordinal()][CELCIUS.ordinal()] =
		new BigDecimal(BigInteger.valueOf(-32));
	convertTerms[FAHRENHEIT.ordinal()][KELVIN.ordinal()] =
		new BigDecimal(BigInteger.valueOf(45967),2);
	convertTerms[FAHRENHEIT.ordinal()][FAHRENHEIT.ordinal()] =
		BigDecimal.ZERO;
	}
	


	
	
}
